﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
namespace Athena.Runtime.Client.HealthChecks.Implementations
{
    using RouteAddress;
    using Options;
    public class TimerHealthCheckDispatch : IHealthCheckDispatch
    {
        private Timer _timerHealthCheckLoop;
        private readonly ConcurrentDictionary<string, HealthCheckEntry> _healthCheckDictionary;

        public TimerHealthCheckDispatch(HealthCheckOption healthCheckOption)
        {

            var timeSpan = TimeSpan.FromSeconds(healthCheckOption.IntervalSeconds);

            _healthCheckDictionary = new ConcurrentDictionary<string, HealthCheckEntry>();
            _timerHealthCheckLoop = new Timer(callback =>
            {
                Check(_healthCheckDictionary.ToArray().Select(x => x.Value));
            }, null, timeSpan, timeSpan);
        }

        public Task Monitor(Route baseRoute)
        {
            return Task.Run(() =>
            {
                _healthCheckDictionary.GetOrAdd(baseRoute.ToString(), new HealthCheckEntry(baseRoute));
            });
        }

        public Task<bool> IsHealth(Route baseRoute)
        {
            return Task.Run(() =>
            {
                var key = baseRoute.ToString();
                return !_healthCheckDictionary.TryGetValue(key, out HealthCheckEntry healthCheckEntry) || healthCheckEntry.Health;
            });
        }

        public Task Mark(Route baseRoute, bool health = false)
        {
            return Task.Run(() =>
            {
                var key = baseRoute.ToString();
                var entry = _healthCheckDictionary.GetOrAdd(key, new HealthCheckEntry(baseRoute, health));
                entry.Health = health;
            });
        }

        private static void Check(IEnumerable<HealthCheckEntry> healthCheckEntries)
        {
            foreach (var entry in healthCheckEntries)
            {
                using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
                {
                    try
                    {
                        socket.Connect(entry.EndPontAddress);
                        entry.Health = true;
                    }
                    catch
                    {
                        entry.Health = false;
                    }
                }
            }
        }
    }
}
